package org.jsola.hr.service.impl;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.jsola.common.SafeKit;
import org.jsola.contract.entity.ContractDO;
import org.jsola.core.Page;
import org.jsola.exception.ParamException;
import org.jsola.hr.common.ExampleKit;
import org.jsola.hr.constant.HrConstants;
import org.jsola.hr.dao.ITaskDAO;
import org.jsola.hr.dto.TaskAddDTO;
import org.jsola.hr.dto.TaskUpdateDTO;
import org.jsola.hr.entity.EmpContractDO;
import org.jsola.hr.entity.EmpInfoDO;
import org.jsola.hr.entity.EmpPaperContractDO;
import org.jsola.hr.entity.TaskDO;
import org.jsola.hr.provider.IContractOverviewProviderService;
import org.jsola.hr.provider.IContractProviderService;
import org.jsola.hr.provider.INoticeProviderService;
import org.jsola.hr.query.TaskQuery;
import org.jsola.hr.service.IEmpContractService;
import org.jsola.hr.service.IEmpInfoService;
import org.jsola.hr.service.IEmpPaperContractService;
import org.jsola.hr.service.ITaskService;
import org.jsola.hr.service.notice.ISendNoticeService;
import org.jsola.hr.task.SendNoticeTask;
import org.jsola.hr.vo.HrNoticeDateSettingVO;
import org.jsola.hr.vo.TaskListVO;
import org.jsola.hr.vo.TaskRunParamVO;
import org.jsola.hr.vo.TaskVO;
import org.jsola.notice.constant.NoticeConstants;
import org.jsola.notice.constant.ReceiveDateTypeEnum;
import org.jsola.notice.entity.NoticeRuleSettingDO;
import org.jsola.notice.vo.NoticeRuleSettingVO;
import org.jsola.user.core.TokenUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import tk.mybatis.mapper.entity.Example;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 人力定时任务
 *
 * @author zhr
 */
@Slf4j
@Service("hrTaskServiceImpl")
public class TaskServiceImpl implements ITaskService {

    @Autowired
    private ITaskDAO taskDAO;

    @Autowired
    private ISendNoticeService sendNoticeService;

    @Autowired
    private INoticeProviderService noticeProviderService;

    @Autowired
    private IEmpInfoService empInfoService;

    @Autowired
    private SendNoticeTask sendNoticeTask;

    @Autowired
    private IContractProviderService contractProviderService;

    @Autowired
    private IEmpPaperContractService empPaperContractService;

    @Autowired
    private IEmpContractService empContractService;

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public TaskVO save(TaskAddDTO taskAddDTO, TokenUser tokenUser) {
        // 转DO
        TaskDO taskDO = taskAddDTO.to(TaskDO.class);
        // 保存
        taskDO = save(taskDO, tokenUser.getSiteId(), tokenUser.getUserId());
        return taskDO.to(TaskVO.class);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public TaskVO saveNoticeTask(TaskAddDTO taskAddDTO, TokenUser tokenUser) {
//        TaskDO taskDO;
//        TaskRunParamVO taskRunParamVO = JSONObject.parseObject(taskAddDTO.getRunParam(), TaskRunParamVO.class);
        //目前只有合同和退休用到了这里，不需要校验重复数据了所以暂时注掉
        // 固定通知
//        if (Boolean.TRUE.equals(taskRunParamVO.getIsFixedNotice())) {

//            taskDO = selectFixedByCompanyId(taskAddDTO.getSubjectType(), taskAddDTO.getSubjectId(), tokenUser.getSiteId());
//            if (taskDO != null) {
//                TaskDO taskUpdate = new TaskDO();
//                taskUpdate.setId(taskDO.getId());
//
//                TaskRunParamVO taskRunParamVOBefore = JSONObject.parseObject(taskDO.getRunParam(), TaskRunParamVO.class);
//                // 原先的业务id
//                String subjectIdsBefore = taskRunParamVOBefore.getSubjectId();
//                String[] split = subjectIdsBefore.split(",");
//                List<String> empIdList = new ArrayList<>(Arrays.asList(split));
//
//                // 本次业务id
//                String subjectId = taskRunParamVO.getSubjectId();
//                String[] split1 = subjectId.split(",");
//                List<String> stringList1 = new ArrayList<>(Arrays.asList(split1));
//                empIdList.addAll(stringList1);
//                String subjectIdListAll = String.join(",", empIdList);
//
//                taskRunParamVO.setSubjectId(subjectIdListAll);
//                taskUpdate.setRunParam(JSON.toJSONString(taskRunParamVO));
//                updateByIdSelective(taskUpdate, tokenUser.getSiteId(), tokenUser.getUserId());
//                return null;
//            }
//        } else {
//            taskDO = selectBySubjectIdAndType(taskAddDTO.getSubjectId(), taskAddDTO.getSubjectType(),
//                    taskRunParamVO.getSubjectId(), tokenUser.getSiteId());
//        }
        // 已存在就不插入,入职提醒不用做唯一校验
        /**
         * 待入职也去掉唯一校验，员工待入职之后，又进行未到岗-重新入职，需要重新发通知
         */
//        if (Objects.nonNull(taskDO)) {
//            if(!NoticeConstants.INDUCTION.equals(taskAddDTO.getSubjectType()) || !NoticeConstants.PRE_INDUCTION.equals(taskAddDTO.getSubjectType())){
//                return null;
//            }
//        }
        return save(taskAddDTO, tokenUser);
    }

    @Override
    public List<TaskDO> batchSave(List<TaskAddDTO> taskAddDTOList, TokenUser tokenUser) {
        if (CollectionUtils.isEmpty(taskAddDTOList)) {
            return new ArrayList<>();
        }
        List<TaskDO> taskDOList = taskAddDTOList.parallelStream()
                .map(TaskAddDTO -> {
                    TaskDO taskDO = TaskAddDTO.to(TaskDO.class);
                    taskDO.preInsert(tokenUser.getUserId());
                    taskDO.setSiteId(tokenUser.getSiteId());
                    return taskDO;
                }).collect(Collectors.toList());
        taskDAO.insertListAndSetId(taskDOList);
        return taskDOList;
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public int update(TaskUpdateDTO taskUpdateDTO, TokenUser tokenUser) {
        //转DO
        TaskDO taskDO = taskUpdateDTO.to(TaskDO.class);
        //根据主键更新，只更新非null值
        return updateByIdSelective(taskDO, tokenUser.getSiteId(), tokenUser.getUserId());
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public int deleteByIds(TokenUser tokenUser, String... taskIds) {
        return logicalDeleteByIds(tokenUser.getSiteId(), tokenUser.getUserId(), (Object[]) taskIds);
    }


    @Override
    public TaskVO selectById(String taskId, String siteId) {
        TaskDO taskDO = selectDOById(taskId, siteId);
        if (taskDO == null) {
            return null;
        }
        return taskDO.to(TaskVO.class);
    }

    @Override
    public TaskDO selectBySubjectIdAndType(String companyId, String subjectType, String subjectId, String siteId) {
        Example example = new Example(TaskDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", siteId)
                .andEqualTo("subjectId", companyId)
                .andEqualTo("subjectType", subjectType)
                .andEqualTo("status", false)
                .andGreaterThan("runDate", new Date());
        List<TaskDO> taskDOList = taskDAO.selectByExample(example);
        if (CollectionUtils.isEmpty(taskDOList)) {
            return null;
        }
        for (TaskDO taskDO : taskDOList) {
            TaskRunParamVO taskRunParamVO = JSONObject.parseObject(taskDO.getRunParam(), TaskRunParamVO.class);
            if (taskRunParamVO != null && subjectId.equals(taskRunParamVO.getSubjectId())) {
                return taskDO;
            }
        }
        return null;
    }

    @Override
    public TaskDO selectFixedByCompanyId(String subjectType, String companyId, String siteId) {
        Example example = new Example(TaskDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", siteId)
                .andEqualTo("subjectId", companyId)
                .andEqualTo("subjectType", subjectType)
                .andEqualTo("status", false);
        List<TaskDO> taskDOList = taskDAO.selectByExample(example);
        if (CollectionUtils.isEmpty(taskDOList)) {
            return null;
        }
        for (TaskDO taskDO : taskDOList) {
            TaskRunParamVO taskRunParamVO = JSONObject.parseObject(taskDO.getRunParam(), TaskRunParamVO.class);
            if (taskRunParamVO != null && Boolean.TRUE.equals(taskRunParamVO.getIsFixedNotice())) {
                return taskDO;
            }
        }
        return null;
    }

    @Override
    public List<TaskListVO> select(TaskQuery taskQuery, String siteId) {
        List<TaskDO> taskDOList = selectDO(taskQuery, siteId);
        if (CollectionUtils.isEmpty(taskDOList)) {
            return taskDOList == null ? null : new ArrayList<>();
        }
        return taskDOList.stream()
                .map(taskDO -> taskDO.to(TaskListVO.class))
                .collect(Collectors.toList());
    }

    @Override
    public int selectCount(TaskQuery taskQuery, String siteId) {
        Example example = buildExample(taskQuery, siteId);
        return taskDAO.selectCountByExample(example);
    }

    @Override
    public Page<TaskListVO> selectPage(TaskQuery taskQuery, String siteId) {
        Example example = buildExample(taskQuery, siteId);
        Page<TaskDO> page = taskDAO.selectPageByExample(example,
                taskQuery.getPageNo(),
                taskQuery.getPageSize());

        return page.to(TaskListVO.class);
    }

    @Override
    public TaskDO selectDOById(String taskId, String siteId) {
        Example example = new Example(TaskDO.class);
        example.and()
                .andEqualTo("id", taskId)
                .andEqualTo("valid", true);

        return taskDAO.selectOneByExample(example);
    }

    @Override
    public List<TaskDO> selectDO(TaskQuery taskQuery, String siteId) {
        Example example = buildExample(taskQuery, siteId);
        return taskDAO.selectByExample(example);
    }

    @Override
    public List<TaskDO> listWaitRuning() {
        Example example = new Example(TaskDO.class);
        example.and()
                .andEqualTo("status", false)
                .andEqualTo("valid", true);

        return taskDAO.selectByExample(example);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public void taskRunEnd(String taskId, TokenUser tokenUser) {
        TaskDO task = selectDOById(taskId, tokenUser.getSiteId());
        if (Objects.nonNull(task)) {
            TaskUpdateDTO taskUpdateDTO = new TaskUpdateDTO();
            taskUpdateDTO.setId(task.getId());
            taskUpdateDTO.setStatus(true);

            update(taskUpdateDTO, tokenUser);
        }
    }

    @Override
    public void runNoticeTask(String taskId) {
        TaskDO task = this.selectDOById(taskId, HrConstants.DEFAULT);
        if (Objects.nonNull(task)) {
            log.info("开始执行定时任务-----------:{}", JSON.toJSONString(task));
            // 拼装当前用户
            TokenUser tokenUser = new TokenUser();
            tokenUser.setSiteId(task.getSiteId());
            tokenUser.setUserId("system");
            // 发送通知
            sendNotice(task);

            // 任务置为已完成
            this.taskRunEnd(task.getId(), tokenUser);
            log.info("定时任务task:{}执行结束", task.getId());
        } else {
            log.info("定时任务发送失败,taskId:{}找不到对应的task数据", taskId);
        }
    }

    @Override
    public int cancel(String subjectId, String subjectType) {
        ParamException.notNull(subjectId, "subjectId不能为空");
        ParamException.notNull(subjectType, "subjectType不能为空");
        Example example = new Example(TaskDO.class);
        example.and().andEqualTo("subjectId", subjectId)
                .andEqualTo("subjectType", subjectType)
                .andEqualTo("valid", true);
        TaskDO taskDO = new TaskDO();
        taskDO.setValid(false);
        return taskDAO.updateByExampleSelective(taskDO, example);
    }

    /**
     * 发送通知
     */
    public void sendNotice(TaskDO task) {
        // 业务类型
        String subjectType = task.getSubjectType();
        //退休合同到期,校验是否可发送
        if (NoticeConstants.RETIRE.equals(subjectType) || NoticeConstants.CONTRACT_EXPIRES.equals(subjectType)) {
            if (!canSend(task)) {
                return;
            }
        }
        // 业务描述
        String description = task.getDescription();
        TaskRunParamVO taskRunParamVO = JSONObject.parseObject(task.getRunParam(), TaskRunParamVO.class);
        int idType;
        switch (subjectType) {
            // 待转正
            case NoticeConstants.PRE_TURN_OFFICIAL:
                // 离职
            case NoticeConstants.RESIGN:
                // 待入职
            case NoticeConstants.PRE_INDUCTION:
                // 入职
            case NoticeConstants.INDUCTION:
                idType = 1;
                sendNoticeService.sendPersonnelNotice(taskRunParamVO.getSubjectId(), idType, subjectType, taskRunParamVO.getIsFixedNotice());
                break;

            // 待离职
            case NoticeConstants.PRE_RESIGN:
                // 待调岗
            case NoticeConstants.PRE_TRANSFER:
                idType = 2;
                sendNoticeService.sendPersonnelNotice(taskRunParamVO.getSubjectId(), idType, subjectType, taskRunParamVO.getIsFixedNotice());

                // 退休
            case NoticeConstants.RETIRE:
                idType = 1;
                sendNoticeService.sendPersonnelNotice(taskRunParamVO.getSubjectId(), idType, subjectType, taskRunParamVO.getIsFixedNotice());
                break;

            // 合同到期
            case NoticeConstants.CONTRACT_EXPIRES:
                sendNoticeService.sendContractExpireNotice(task, taskRunParamVO.getSubjectId(), subjectType, taskRunParamVO.getIsFixedNotice());
                break;
            // 增减员
            case NoticeConstants.INCREMENT:
                String[] split = task.getSubjectId().split(",");
                sendNoticeService.sendIncreaseDecreaseNotice(split[0], task.getRunType(), split.length, false);
                break;
            // 甲方端服务到期
            case NoticeConstants.JF_CONTRACT:
                sendNoticeService.sendJfContractNotice(task.getRunType(), task.getSubjectId());
                break;
            default:
                log.error("发送通知失败：无此类型-->{}", subjectType);
                break;
        }
    }

    /**
     * 运行时间和需要运行时间是否相符
     *
     * @param task 任务
     * @return 真假
     */
    private boolean canSend(TaskDO task) {
        Date date = null;
        TaskRunParamVO taskRunParamVO = JSONObject.parseObject(task.getRunParam(), TaskRunParamVO.class);
        String[] subjectIds = taskRunParamVO.getSubjectId().split(",");
        if (subjectIds.length == 0) {
            log.info("通知id{}发送失败：task表中RunParam为空", task.getId());
            return false;
        }
        //运行时间
        Date runDate = task.getRunDate();

        NoticeRuleSettingVO ruleSettingVO = noticeProviderService.selectByTypeCodeAndCompany(HrConstants.HR_SUBJECT_TYPE,
                task.getSubjectType(), task.getSubjectId());
        if (ruleSettingVO == null || ruleSettingVO.getId() == null) {
            log.info("通知id{}发送失败：该公司通知设置不存在", task.getId());
            return false;
        }
        if (!ruleSettingVO.getEnableStatus()) {
            log.info("通知id{}发送失败：该公司通知设置已关闭", task.getId());
            return false;
        }
        Integer receiveDateType = ruleSettingVO.getReceiveDateType();

        //需要运行时间
        if (NoticeConstants.RETIRE.equals(task.getSubjectType())) {
            //退休日期
            EmpInfoDO empInfoDO = empInfoService.selectById(subjectIds[0]);

            if (empInfoDO == null || empInfoDO.getId() == null) {
                log.info("通知id{}发送失败：退休人员不存在", task.getId());
                return false;
            }
            if (empInfoDO.getRetirementDate() != null) {
                date = empInfoDO.getRetirementDate();
            } else if (!StringUtils.isEmpty(empInfoDO.getBirthday()) && !StringUtils.isEmpty(empInfoDO.getRetirementAge())) {
                date = DateUtil.offset(DateUtil.parse(empInfoDO.getBirthday()), DateField.YEAR, empInfoDO.getRetirementAge());
            } else {
                log.info("通知id{}发送失败：员工没有录入具体的退休时间", task.getId());
                return false;
            }
        } else if (NoticeConstants.CONTRACT_EXPIRES.equals(task.getSubjectType())) {
            //合同到期日期
            String contractId = taskRunParamVO.getSubjectId();
            Long contractId1 = SafeKit.getLong(contractId);
            if (contractId1 != null) {
                ContractDO contractDO = contractProviderService.selectById(contractId1);
                date = contractDO.getEndDate();
            } else {
                EmpPaperContractDO empPaperContractDO = empPaperContractService.selectDOById(contractId);
                if (empPaperContractDO == null) {
                    EmpContractDO empContractDO = empContractService.selectDOById(contractId);
                    if (empContractDO == null) {
                        log.info("合同到期提醒，查询不到合同信息，合同id：{}", contractId);
                        return false;
                    } else {
                        date = empContractDO.getEndTime();
                    }
                } else {
                    date = empPaperContractDO.getEndTime();
                }
            }
        }
        if (date == null) {
            return false;
        }
        HrNoticeDateSettingVO hrNoticeDateSettingVO = sendNoticeTask.parseDate(ruleSettingVO.to(NoticeRuleSettingDO.class));
        if (ReceiveDateTypeEnum.RIGHT_NOW.getValue().equals(receiveDateType)) {
            if (StringUtils.isEmpty(hrNoticeDateSettingVO.getAppointTime())) {
                hrNoticeDateSettingVO.setAppointTime("9:00:00");
            }
            String[] split = hrNoticeDateSettingVO.getAppointTime().split(":");
            date = DateUtil.offsetSecond(date, Integer.parseInt(split[0]) * 60 * 60 + Integer.parseInt(split[1]) * 60 + Integer.parseInt(split[2]));
        } else if (ReceiveDateTypeEnum.BEFORE.getValue().equals(receiveDateType)) {
            date = DateUtil.offsetDay(date, -hrNoticeDateSettingVO.getNumber());
            if (StringUtils.isEmpty(hrNoticeDateSettingVO.getAppointTime())) {
                hrNoticeDateSettingVO.setAppointTime("9:00:00");
            }
            String[] split = hrNoticeDateSettingVO.getAppointTime().split(":");
            date = DateUtil.offsetSecond(date, Integer.parseInt(split[0]) * 60 * 60 + Integer.parseInt(split[1]) * 60 + Integer.parseInt(split[2]));
        } else if (ReceiveDateTypeEnum.FIXED.getValue().equals(receiveDateType)) {
            Date beginDate = DateUtil.offsetDay(DateUtil.beginOfMonth(new Date()), hrNoticeDateSettingVO.getDay() - 1);
            Date endDate = DateUtil.offsetMonth(beginDate, 1);
            if (date.before(beginDate) || date.after(endDate)) {
                log.info("通知id{}发送失败：发送时间不符合需要发送的条件", task.getId());
                return false;
            }
            String[] split = hrNoticeDateSettingVO.getTime().split(":");
            date = DateUtil.offsetSecond(beginDate, Integer.parseInt(split[0]) * 60 * 60 + Integer.parseInt(split[1]) * 60 + Integer.parseInt(split[2]));
        }
        int minute = 0;
        try {
            minute = (int) DateUtil.between(date, runDate, DateUnit.MINUTE, true);
        } catch (Exception e) {
            log.info("long转int异常");
            return false;
        }
        //运行时间和需要运行的时间相差两分钟内就发送
        if (minute <= 2) {
            return true;
        } else {
            log.info("运行时间和需要运行的时间相差超过两分钟");
            return false;
        }
    }


    /**
     * 根据查询参数，构建example
     *
     * @param taskQuery 查询参数
     * @param siteId    所属站点id
     * @return example
     */
    private Example buildExample(TaskQuery taskQuery, String siteId) {
        Example example = new Example(TaskDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", siteId);
        if (taskQuery.getSubjectType() != null) {
            example.and().andEqualTo("subjectType", taskQuery.getSubjectType());
        }
        if (taskQuery.getSubjectId() != null) {
            example.and().andEqualTo("subjectId", taskQuery.getSubjectId());
        }
        if (taskQuery.getDescription() != null) {
            example.and().andEqualTo("description", taskQuery.getDescription());
        }
        if (taskQuery.getRunBeanName() != null) {
            example.and().andEqualTo("runBeanName", taskQuery.getRunBeanName());
        }
        if (taskQuery.getRunDateStart() != null &&
                taskQuery.getRunDateEnd() != null) {
            example.and().andBetween("runDate", taskQuery.getRunDateStart(),
                    taskQuery.getRunDateEnd());
        }
        if (taskQuery.getRunType() != null) {
            example.and().andEqualTo("runType", taskQuery.getRunType());
        }
        if (taskQuery.getStatus() != null) {
            example.and().andEqualTo("status", taskQuery.getStatus());
        }
        if (taskQuery.getRunParam() != null) {
            example.and().andEqualTo("runParam", taskQuery.getRunParam());
        }
        // 排序
        ExampleKit.setExampleOrder(example, taskQuery.getOrders());
        return example;
    }
}




